#include "common.hpp"

#include <set> 
static const char *optString = "r:p:h?";

static const struct option longOpts[] = {
	{ "help", no_argument, NULL, 'h' },
	{ "refresh", required_argument, NULL, 'r'},
	{ 0,0,0,0 }
};

template <class OSTREAM>
class mostrar_ocupados : public procesar_aparato {
	protected:
		OSTREAM& os;
		size_t columnas;
		size_t actual;

		virtual bool _do (sAparato& aparato) {
			os << std::setw(3) << aparato.id << " (";
			os << std::setw(2) << std::setfill('0') << aparato.tipo << std::setfill(' ') << "): ";
			if (aparato.entrenador == 0)
				os << std::left << std::setw(16) << "libre." << std::right;
			else {
				os << std::setfill('0') << std::setw(5) << aparato.usuario << std::setfill(' ');
				if (aparato.invitado)
					os << "* con ";
				else
					os << "  con ";
				os << std::setfill('0') << std::setw(5) << aparato.entrenador << std::setfill(' ');
			}

			if ( ++actual == columnas) {
				os << "\n";
				actual = 0;
			} else
				os << "; ";

			return true;
		}
	public:
		void reiniciar_columna_actual() {
			this->actual = 0;
		}

		mostrar_ocupados (OSTREAM& ostream, size_t columnas) : os(ostream) {
			this->columnas = (columnas > 0) ? columnas : 1;
		}
}; // class mostrar

std::string fecha_y_hora () {
	char strTime[20];
	struct timeval utime;
	gettimeofday(&utime,NULL);

	strftime(strTime, 20, "%Y/%m/%d %T", localtime(&(utime.tv_sec)));
	strTime[19] = '\0';
	
	return std::string(&strTime[0]);
}

int main (int argc, char* const argv[]) {
	// Objetos IPC.
	monitor_aparatos_with_exceptions aparatos(ftok(BASE_NAME,15), ftok(BASE_NAME,14),0);
	ipc::semaphore_with_exceptions pantalla_gigante_mostrar ( ftok(BASE_NAME,25),0 );
	ipc::bounded_counter_with_exceptions pantalla_gigante_aviso ( ftok(BASE_NAME,27), ftok(BASE_NAME,26),0 );

	// Parametros.
	long refresco = 30;
	bool seguir = true;

	// Variables.
	int longIndex = 0;
	int option = 0;
	bool ayuda = false;

	// Interpretación de parametros.
	while ( (option = getopt_long(argc,argv,optString,longOpts,&longIndex) ) != EOF ) {
		switch (option) {
			case '?':
			case 'h':
			case ':':
				ayuda = true;
				break;
			case 'r':
				refresco = parse<size_t>(optarg,0);
				break;
			default:
				break;
		}
	}

	if ( (ayuda) || (refresco <= 0) ) {
		std::cerr << "Proceso de actualización de la pantalla gigante." << std::endl;
		std::cerr << "\tUso: " << argv[0] << " [-h] <-refresh entero>" << std::endl;
		std::cerr << "\tParametros:" << std::endl;
		std::cerr << "\t\t-h,--help:\tMostrar este mensaje de ayuda." << std::endl;
		std::cerr << "\t\t-r,--refresh:\tCantidad de unidades de tiempo a esperar." << std::endl;
		std::cerr << "\t\tUnidad de tiempo actual = " << SEGUNDO << "us." << std::endl;

		return (ayuda) ? 0 : -1;
	}

//	srand( time(NULL) + getpid() );

	logger.setColor("\033[0;37m");
	refresco*= SEGUNDO; // Para no recalcularlo en cada ciclo.

	// Subproceso encargado de manejar el refresco de la pantalla cada cierto tiempo.
	if ( fork() == 0) {
		execlp("./pantalla_gigante_timer","pantalla_gigante_timer",c_parse(refresco),(char*)NULL);
	}

	try {
		aparatos.get(PERMITS);
		pantalla_gigante_mostrar.get(PERMITS);
		pantalla_gigante_aviso.get(PERMITS);

		logger << "Pantalla gigante: Inicio." << nl;

		std::stringstream buffer;
		mostrar_ocupados<std::stringstream> m(buffer,3);
		utils::logger_stream::multi_line multi_line_buffer("");

		size_t aparatos_checkpoint = 0;
		size_t aparatos_actual = 0;

		while (seguir) {
			// La pantalla no controla que existan cambios. Alguien más levanto la bandera indicando que hay que actualizar los datos mostrados.
			pantalla_gigante_mostrar.wait();
						
			logger << "Pantalla gigante: ================ limpiar pantalla  ================ " << nl;
//			logger << "\033[2J" << nl;
			logger << "Pantalla gigante: " << fecha_y_hora() << nl;
			aparatos_checkpoint = aparatos_actual;
			buffer.str("");
			buffer.clear();
			m.reiniciar_columna_actual();
			aparatos.for_each_aparato(m);

			multi_line_buffer.set(buffer.str());
			logger << "Pantalla gigante: " << multi_line_buffer << nl;

			// La pantalla indica que refresco la pantalla y espera que otro avise de cambios.
			pantalla_gigante_aviso.setActual(0);
		}

	} catch (ipc::exception& e) { 
		logger << "Pantalla gigante: " << e.what() << nl;
		exit(-1);
	}

	logger << "Pantalla gigante: Fin." << nl;
	return 0;
}

